/*
Copyright (c) 2011, Ludovic BADIN, http://www.flex4you.fr

All rights reserved.

Redistribution and use in source and binary forms, with or without 
modification, are permitted provided that the following conditions are
met:

* Redistributions of source code must retain the above copyright notice, 
this list of conditions and the following disclaimer.

* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the 
documentation and/or other materials provided with the distribution.

* Neither the name of Ludovic BADIN nor the names of its 
contributors may be used to endorse or promote products derived from 
this software without specific prior written permission.

THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package com.ludovicbadin.elink.soap
{
	import com.ludovicbadin.elink.soap.events.FaultSOAPEvent;
	import com.ludovicbadin.elink.soap.events.ResultSOAPEvent;
	import com.ludovicbadin.elink.soap.model.AttributXML;
	import com.ludovicbadin.elink.soap.model.DocumentXML;
	import com.ludovicbadin.elink.soap.model.ErrorXML;
	import com.ludovicbadin.elink.soap.model.ObjectValueXML;
	import com.ludovicbadin.elink.soap.model.ObjectXML;
	import com.ludovicbadin.elink.soap.utils.TargetEnum;
	
	import flash.events.EventDispatcher;
	import flash.utils.getQualifiedClassName;
	import flash.xml.XMLDocument;
	
	import mx.collections.ArrayCollection;
	import mx.controls.dataGridClasses.DataGridColumn;
	import mx.logging.ILogger;
	import mx.logging.Log;
	import mx.logging.LogEventLevel;
	import mx.logging.targets.TraceTarget;
	import mx.rpc.events.FaultEvent;
	import mx.rpc.events.ResultEvent;
	import mx.rpc.http.HTTPService;
	import mx.rpc.xml.SimpleXMLDecoder;
	import mx.utils.ObjectProxy;
	
	/**
	 * Elink SOAP Proxy for Flex
	 * Group all functions of LinkSession
	 * @author Ludovic BADIN
	 * @site http://www.flex4you.fr
	 * @langversion 3.0
	 * @playerversion Flash 10
	 * @productversion Flex 4 / ElinkFlexSOAPProxy v1.0.2
	 */ 
	
	public class LinkSession extends EventDispatcher
	{
		/**
		 * Define log for API
		 */ 
		private static var logger:ILogger = Log.getLogger("com.ludovicbadin.elink.soap.LinkSession");
		
		/**
		 * Define HTTPService to send SOAP enveloppe
		 */ 
		private static var service:HTTPService;
		
		/**
		 * Define response of XML enveloppe
		 */ 
		private static var xmlResponseEnveloppe:XML;
		
		/**
		 * Contains XML document to send
		 */ 
		private static var documentXML:DocumentXML;
		
		/**
		 * Unique ID for each operations
		 */ 
		private static var uniqueSOAPResponseID:Number;
		
		/**
		 * Success function callback defined by client application
		 */ 
		private static var successFunction:Function;
		
		/**
		 * Failed function callback defined by client application
		 */ 
		private static var failedFunction:Function;
		
		/**
		 * List of containers to retrieve
		 */
		private static var datacontainers:Array;
		
		/**
		 * Decoder to convert XML to Object 
		 */
		private static var decoder:SimpleXMLDecoder = new SimpleXMLDecoder(true);
		
		/**
		 * Class constructor
		 * Instance of HTTPService
		 * Content type is XML, method is post and result format is e4x
		 * @param String eLink webpages url (sBase)
		 * @param showBusyCursor to change cursor skin during a service call
		 */ 
		public function LinkSession(sBase:String, showBusyCursor:Boolean = true)
		{
			service 				= new HTTPService();
			service.url 			= sBase + "api";
			service.contentType 	= "application/xml";
			service.method			= "POST";
			service.resultFormat	= "e4x";
			service.showBusyCursor	= showBusyCursor;
			uniqueSOAPResponseID 	= 0;
			datacontainers			= new Array();
			//Add event for result and fault
			service.addEventListener(ResultEvent.RESULT, elinkService_resultHandler);
			service.addEventListener(FaultEvent.FAULT, elinkService_faultHandler);		
			if(Log.isDebug()){
				logger.debug("You are currently connected to " + sBase + " server");
			}
		}
		
		/**
		 * Use to log SOAP request
		 * @param int level of logs
		 * @param filter list of filter to apply (ex: mx.rpc.*)
		 */ 
		public static function setLogging(level:int = LogEventLevel.ALL, ... filter):void {
			var logTarget:TraceTarget = new TraceTarget();
			if(filter.length > 0){
				logTarget.filters=filter;
			}
			logTarget.level = level;
			logTarget.includeDate = true;
			logTarget.includeTime = true;
			logTarget.includeCategory = true;
			logTarget.includeLevel = true;
			Log.addTarget(logTarget);
			if(Log.isDebug()){
				logger.debug("Logs are setted");
			}
		}
		
		/**
		 * Call if request is success. Dispatch a new Event (ResultSOAPEvent with response)
		 * @param ResultEvent
		 * @return void
		 */ 
		private function elinkService_resultHandler(event:ResultEvent):void{
			xmlResponseEnveloppe = event.result as XML;
			if(responseTreatment(xmlResponseEnveloppe)){
				dispatchEvent(new ResultSOAPEvent(new XMLDocument(xmlResponseEnveloppe.toString()), uniqueSOAPResponseID));
			}
			if(Log.isDebug()){
				logger.debug("We have received a successfull response from server");
			}
		}
		
		/**
		 * Call if request is failed. Dispatch a new Event (FaultSOAPEvent with ErrorXML object)
		 * @param FaultEvent
		 * @return void
		 */ 
		private function elinkService_faultHandler(event:FaultEvent):void{
			var faultError:ErrorXML = new ErrorXML(String(event.fault.errorID), event.fault.faultDetail, event.fault.faultString);
			dispatchEvent(new FaultSOAPEvent(faultError));
			if(Log.isDebug()){
				logger.debug("We have received a failed response from server");
			}
		}
		
		/**
		 * Initialize SOAP Enveloppe. Create a new XML Document
		 * @return void
		 */ 
		public static function initSOAPEnveloppe():void{
			documentXML	= new DocumentXML();
		}
		
		/**
		 * Re-initialize all params
		 * @return void
		 */ 
		private static function initAttributSOAPEnveloppe():void{
			datacontainers = new Array();
			xmlResponseEnveloppe = null;
		}
		
		/**
		 * Retrieve param from eLink.
		 * @param type Type of param to retrieve (ex: aliaslist)
		 * @return Number
		 */ 
		public static function getParamSOAPSession(... type):Number {
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common", new AttributXML("ID", getUniqueSOAPResponseID()));
			for (var i:int=0; i<type.length; i++){
				datacontainers.push(type[i]);
				common.objects.addItem(new ObjectXML("datacontainer", new AttributXML("type", type[i])));
			}
			documentXML.objects.addItem(common);
			if(Log.isDebug()){
				logger.debug("DataContainer Array value :" + datacontainers.toString());
			}
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Retrieve current data from eLink.
		 * @param type (String) Type of params to retrieve (ex: currentid)
		 * @return Number Request ID
		 */ 
		public static function getDataSOAPSession(... type):Number {
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common", new AttributXML("ID", getUniqueSOAPResponseID()));
			for (var i:int=0; i<type.length; i++){
				common.objects.addItem(new ObjectXML("data", new AttributXML("type", type[i])));
			}
			documentXML.objects.addItem(common);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to know if session is open or not a user
		 * @return Number Request ID
		 */ 
		public static function sessionOpened():Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common", new AttributXML("ID", getUniqueSOAPResponseID()));
			common.objects.addItem(new ObjectXML("data", new AttributXML("type", "issessionopened")));
			documentXML.objects.addItem(common);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to authenticate a user
		 * @param String user login
		 * @param String user password
		 * @param String database alias (path)
		 * @return Number Request ID
		 */ 
		public static function login(user:String, password:String, dbPath:String):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var login:ObjectXML = new ObjectXML("elink:login");
			login.attribut.addItem(new AttributXML('login', user));
			login.attribut.addItem(new AttributXML('password', password));
			login.attribut.addItem(new AttributXML('dbpath', dbPath));
			login.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			documentXML.objects.addItem(login);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to logout a user
		 * @return void
		 */ 
		public static function logout():void{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var logout:ObjectXML = new ObjectXML("elink:logout");
			documentXML.objects.addItem(logout);
		}
		
		/**
		 * Use to searc user in LDAP
		 * @param String host of ldap server
		 * @param String username to access in ldap server
		 * @param String password to access in ldap server
		 * @param String basename of server
		 * @param String filter
		 * @param String scope
		 * @param Boolean password encrypt or not
		 * @return Number request ID
		 */ 
		public static function searchLdap(host:String, username:String, password:String, basename:String, filter:String, scope:String, encrypt:Boolean, portnumber:String):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var ldap:ObjectXML = new ObjectXML("elink:configure");
			var datacontainer:ObjectXML = new ObjectXML("datacontainer");
			datacontainer.attribut.addItem(new AttributXML('type', 'ldapsearch'));
			datacontainer.attribut.addItem(new AttributXML('host', host));
			datacontainer.attribut.addItem(new AttributXML('portnumber', portnumber));	
			datacontainer.attribut.addItem(new AttributXML('username', username));	
			datacontainer.attribut.addItem(new AttributXML('password', password));	
			datacontainer.attribut.addItem(new AttributXML('basename', basename));		
			if(filter != null){
				datacontainer.attribut.addItem(new AttributXML('filter', filter));
			}
			if(scope != null){
				datacontainer.attribut.addItem(new AttributXML('scope', scope));
			}
			datacontainer.attribut.addItem(new AttributXML('encrypt', encrypt.toString()));
			datacontainer.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			ldap.objects.addItem(datacontainer);
			documentXML.objects.addItem(ldap);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to consult an entity (contact, company, project, ...)
		 * Targets often used are CY, CONT, PROJ, PROD, DOC ...
		 * @param String target
		 * @param String id of entity
		 * @return Number Request ID
		 */
		public static function consult(target:TargetEnum, key:String):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var consult:ObjectXML = new ObjectXML("elink:consult");
			consult.attribut.addItem(new AttributXML('target', target.getTargetName()));
			consult.attribut.addItem(new AttributXML('key', key));
			consult.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			consult.objects.addItem(new ObjectXML("datacontainer", new AttributXML("type", "master")));
			documentXML.objects.addItem(consult);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to consult detail an entity (contact, company, project, ...)
		 * Targets often used are CY, CONT, PROJ, PROD, DOC ...
		 * @param TargetEnum target
		 * @param String id of entity
		 * @param details List of details
		 * @return Number Request ID
		 */
		public static function consultDetail(target:TargetEnum, key:String, ... details):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var consult:ObjectXML = new ObjectXML("elink:consult");
			consult.attribut.addItem(new AttributXML('target', target.getTargetName()));
			consult.attribut.addItem(new AttributXML('key', key));
			consult.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			for (var i:int=0; i<details.length; i++){
				if(details[i] is TargetEnum){
					datacontainers.push((details[i] as TargetEnum).getTargetName());
				}
				else{
					datacontainers.push(details[i]);
				}
				consult.objects.addItem(new ObjectXML("datacontainer", new AttributXML("type", datacontainers[datacontainers.length-1])));
			}
			if(Log.isDebug()){
				logger.debug("DataContainer Array value :" + datacontainers.toString());
			}
			documentXML.objects.addItem(consult);
			return uniqueSOAPResponseID;
		}
		
		public static function updateDetail(target:TargetEnum, key:String, detail:TargetEnum, detailkey:String, fields:ArrayCollection, apply:String = "keep"):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var edit:ObjectXML = new ObjectXML("elink:edit");
			var updateDetail:ObjectXML = new ObjectXML("updatedetail");
			edit.attribut.addItem(new AttributXML('target', target.getTargetName()));
			edit.attribut.addItem(new AttributXML('key', key));
			edit.attribut.addItem(new AttributXML('apply', apply));
			edit.attribut.addItem(new AttributXML('closecontext', "true"));
			edit.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			updateDetail.attribut.addItem(new AttributXML('detail', detail.getTargetName()));
			updateDetail.attribut.addItem(new AttributXML('detailkey', detailkey));
			updateDetail.objects.addAll(fields);
			edit.objects.addItem(updateDetail);
			documentXML.objects.addItem(edit);
			return uniqueSOAPResponseID;
		}
		
		public static function insertDetail(target:TargetEnum, key:String, detail:TargetEnum, detailkey:String, linkMainCy:String = "false", apply:String = "keep"):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var edit:ObjectXML = new ObjectXML("elink:edit");
			var insertDetail:ObjectXML = new ObjectXML("insertdetail");
			edit.attribut.addItem(new AttributXML('target', target.getTargetName()));
			edit.attribut.addItem(new AttributXML('key', key));
			edit.attribut.addItem(new AttributXML('apply', apply));
			edit.attribut.addItem(new AttributXML('closecontext', "true"));
			edit.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			insertDetail.attribut.addItem(new AttributXML('detail', detail.getTargetName()));
			insertDetail.attribut.addItem(new AttributXML('detailkey', detailkey));
			insertDetail.attribut.addItem(new AttributXML('maincy', linkMainCy));
			edit.objects.addItem(insertDetail);
			documentXML.objects.addItem(edit);
			return uniqueSOAPResponseID;
		}
		
		public static function deleteDetail(target:TargetEnum, key:String, detail:TargetEnum, detailkey:String, apply:String = "keep"):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var edit:ObjectXML = new ObjectXML("elink:edit");
			var deleteDetail:ObjectXML = new ObjectXML("deletedetail");
			edit.attribut.addItem(new AttributXML('target', target.getTargetName()));
			edit.attribut.addItem(new AttributXML('key', key));
			edit.attribut.addItem(new AttributXML('apply', apply));
			edit.attribut.addItem(new AttributXML('closecontext', "true"));
			edit.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			deleteDetail.attribut.addItem(new AttributXML('detail', detail.getTargetName()));
			deleteDetail.attribut.addItem(new AttributXML('detailkey', detailkey));
			edit.objects.addItem(deleteDetail);
			documentXML.objects.addItem(edit);
			return uniqueSOAPResponseID;
		}
		
		public static function addOrModifyFavorite(target:TargetEnum, key:String, text:String):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common");
			var data:ObjectXML = new ObjectXML("data");
			data.attribut.addItem(new AttributXML('type', 'addormodifyfavorite'));
			data.attribut.addItem(new AttributXML('target', target.getTargetName()));
			data.attribut.addItem(new AttributXML('entitykey', key));
			data.attribut.addItem(new AttributXML('text', text));
			data.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			common.objects.addItem(data);
			documentXML.objects.addItem(common);
			return uniqueSOAPResponseID;
		}
		
		public static function consultFavorites():Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common");
			var data:ObjectXML = new ObjectXML("datacontainer");
			data.attribut.addItem(new AttributXML('type', 'favoritelist'));
			data.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			common.objects.addItem(data);
			documentXML.objects.addItem(common);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to search an entity (contact, company, project, ...)
		 * Targets often used are CY, CONT, PROJ, PROD, DOC, ...
		 * @param String target
		 * @param String searchField (NAME, FLD, ...)
		 * @param String value of research
		 * @param Boolean optionsOwner to know if we have to research entity created by the user connected
		 * @param Boolean optionsContains is a LIKE SQL operator (%VALUE%) or if false it's only (VALUE)
		 * @return Number Request ID
		 */
		public static function search(target:TargetEnum, searchField:String, value:String, optionsOwn:Boolean = false, optionsContains:Boolean = true):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var search:ObjectXML = new ObjectXML("elink:search");
			search.attribut.addItem(new AttributXML('target', target.getTargetName()));
			search.attribut.addItem(new AttributXML('searchfield', searchField.toUpperCase()));
			search.attribut.addItem(new AttributXML('value', value));
			search.attribut.addItem(new AttributXML("own", String(optionsOwn)));
			search.attribut.addItem(new AttributXML("contains", String(optionsContains)));
			search.attribut.addItem(new AttributXML("ID", getUniqueSOAPResponseID()));
			search.objects.addItem(new ObjectXML("datacontainer", new AttributXML("type", "master")));
			documentXML.objects.addItem(search);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to edit an entity (contact, company, project, ...)
		 * Targets often used are CY, CONT, PROJ, PROD, DOC, ...
		 * @param TargetEnum target
		 * @param String key of target (ID Database)
		 * @param ArrayCollection fields to update (TYPE: ObjectValueXML)
		 * @param String Type of apply (Keep or True)
		 * 		- Keep Changes (if any) are entered into the database. The current editid is still valid for furtherchanges.
		 * 		- True Changes (if any) are entered into the database. The current editid is discarded. No further changes may be made using this editid.
		 * @return Number Request ID
		 */
		public static function edit(target:TargetEnum, key:String, fields:ArrayCollection, apply:String = 'keep'):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var edit:ObjectXML = new ObjectXML("elink:edit");
			edit.attribut.addItem(new AttributXML('target', target.getTargetName()));
			edit.attribut.addItem(new AttributXML('key', key));
			edit.attribut.addItem(new AttributXML('apply', apply));
			var update:ObjectXML = new ObjectXML("UPDATE");
			update.objects.addAll(fields);
			edit.objects.addItem(update);
			documentXML.objects.addItem(edit);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to edit relation of an entity (contact, company, project, ...)
		 * Targets often used are CY, CONT, PROJ, PROD, DOC, ...
		 * @param TargetEnum target
		 * @param String key of target (ID Database)
		 * @param TargetEnum detail target
		 * @param String key of detail target (ID Database)
		 * @param ArrayCollection fields to update (TYPE: ObjectValueXML)
		 * @param String Type of apply (Keep or True)
		 * 		- Keep Changes (if any) are entered into the database. The current editid is still valid for furtherchanges.
		 * 		- True Changes (if any) are entered into the database. The current editid is discarded. No further changes may be made using this editid.
		 * @return Number Request ID
		 */
		public static function editRelation(target:TargetEnum, key:String, detailtarget:TargetEnum, detailkey:String, fields:ArrayCollection, apply:String = 'keep'):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var edit:ObjectXML = new ObjectXML("elink:edit");
			edit.attribut.addItem(new AttributXML('target', target.getTargetName()));
			edit.attribut.addItem(new AttributXML('key', key));
			edit.attribut.addItem(new AttributXML('detail', detailtarget.getTargetName()));
			edit.attribut.addItem(new AttributXML('detailkey', detailkey));
			edit.attribut.addItem(new AttributXML('apply', apply));
			var update:ObjectXML = new ObjectXML("UPDATE");
			update.objects.addAll(fields);
			edit.objects.addItem(update);
			documentXML.objects.addItem(edit);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to add link between two entities (contact, company, project, ...)
		 * Targets often used are CY, CONT, PROJ, PROD, DOC, ...
		 * @param TargetEnum target
		 * @param String key of target (ID Database)
		 * @param TargetEnum detail target
		 * @param String key of detail target (ID Database)
		 * @param String ignoreExisting by default true. If true link between two relations is updated otherwise if false an error occured and it's impossible to add the link
		 * @return Number Request ID
		 */
		public static function addLink(target:TargetEnum, key:String, detailtarget:TargetEnum, detailkey:String, ignoreExisting:String = 'true'):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common");
			var addLink:ObjectXML = new ObjectXML("data");
			addLink.attribut.addItem(new AttributXML('type', 'addlink'));			
			addLink.attribut.addItem(new AttributXML('key', key));
			addLink.attribut.addItem(new AttributXML('detailkey', detailkey));
			addLink.attribut.addItem(new AttributXML('laptarget', target.getTargetName()));
			addLink.attribut.addItem(new AttributXML('detaillaptarget', detailtarget.getTargetName()));
			addLink.attribut.addItem(new AttributXML('ignoreexisting', ignoreExisting));
			common.objects.addItem(addLink);
			documentXML.objects.addItem(common);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use this function to delete link between two entities (contact, company, project, ...)
		 * Targets often used are CY, CONT, PROJ, PROD, DOC, ...
		 * @param TargetEnum target
		 * @param String key of target (ID Database)
		 * @param TargetEnum detail target
		 * @param String key of detail target (ID Database)
		 * @param String ignoreExisting by default true. If true link between two relations is deleted otherwise if false an error occured and it's impossible to delete the link
		 * @return Number Request ID
		 */
		public static function deleteLink(target:TargetEnum, key:String, detailtarget:TargetEnum, detailkey:String, ignoreExisting:String = 'true'):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common");
			var deleteLink:ObjectXML = new ObjectXML("data");
			deleteLink.attribut.addItem(new AttributXML('type', 'deletelink'));			
			deleteLink.attribut.addItem(new AttributXML('key', key));
			deleteLink.attribut.addItem(new AttributXML('detailkey', detailkey));
			deleteLink.attribut.addItem(new AttributXML('laptarget', target.getTargetName()));
			deleteLink.attribut.addItem(new AttributXML('detaillaptarget', detailtarget.getTargetName()));
			common.objects.addItem(deleteLink);
			documentXML.objects.addItem(common);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use function to retrieve data from table choose
		 * @param String name of table
		 * @return Number id of command
		 */ 
		public static function getListFromTable(tableName:String):Number{
			return executeSQLQuery("SELECT * FROM " + tableName);
		}
		
		/**
		 * Use function to have list of users who are in group
		 * @param Number id of group
		 * @return Number id of command
		 */ 
		public static function usersInGroup(groupID:Number):Number {
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common");
			var group:ObjectXML = new ObjectXML("data");
			group.attribut.addItem(new AttributXML('type', 'useringroup'));		
			group.attribut.addItem(new AttributXML('idgroup', String(groupID)));	
			common.objects.addItem(group);
			documentXML.objects.addItem(common);
			return uniqueSOAPResponseID;
		}
		
		/**
		 * Use function to know if current user is in group or not
		 * @param Number id of user
		 * @param String name of group
		 * @return Number id of command
		 */ 
		public static function userBelongToGroup(userId:Number, groupName:String):Number {
			return executeSQLQuery("SELECT GRP.IDUSER2 FROM USR_USR GRP WHERE GRP.IDUSER IN (SELECT U.IDUSER FROM USERS U WHERE U.NAME = '"+groupName+"' AND U.KIND = 1) AND GRP.IDUSER2 = " + userId);
		}
		
		/**
		 * Use function to know if all group of user
		 * @param Number id of user
		 * @return Number id of command
		 */ 
		public static function groupsOfUser(userId:Number):Number {
			return executeSQLQuery("SELECT NAME, IDUSER FROM USERS WHERE IDUSER IN (SELECT GRP.IDUSER FROM SU.USR_USR GRP LEFT JOIN SU.USERS USR ON USR.IDUSER = GRP.IDUSER2 WHERE USR.IDUSER = "+ userId +")");
		}
		
		/**
		 * Execute SQL query
		 * @param String is sql query
		 * @param String is parameters of query
		 * @param recordCoutn is limitation of query
		 * @return Number id of command
		 */ 
		public static function executeSQLQuery(sql:String, queryParams:String = '', recordCount:Number = 0):Number{
			if(documentXML == null){
				initSOAPEnveloppe();
			}
			var common:ObjectXML = new ObjectXML("elink:common", new AttributXML("ID", getUniqueSOAPResponseID()));
			var container:ObjectXML = new ObjectXML("datacontainer", new AttributXML('type',"executesqlquery"));
			var sqlObj:ObjectValueXML = new ObjectValueXML("SQL", sql);
			var queryParamsObj:ObjectValueXML = new ObjectValueXML("QueryParams", queryParams);
			container.objects.addItem(sqlObj);
			container.objects.addItem(queryParamsObj);
			common.objects.addItem(container);
			documentXML.objects.addItem(common);
			return uniqueSOAPResponseID;
		}
		
		
		/**
		 * Send SOAP enveloppe created to eLink API (it's last operation)
		 * @param LinkSession instance of service initiated
		 * @param Function Success function callback
		 * @param Function Failed function callback
		 * @return void
		 */ 
		public static function executeBatch(serviceCalled:LinkSession, success:Function = null, failed:Function = null):void{
			service.send(new XML(documentXML.toString()));
			if(Log.isDebug()){
				logger.debug("XML Document has been sent");
			}
			initSOAPEnveloppe();
			manageListeners(serviceCalled, success, failed);
		}
		
		/**
		 * Send SOAP enveloppe by string
		 * @param LinkSession instance of service initiated
		 * @param String enveloppe to send
		 * @param Function Success function callback
		 * @param Function Failed function callback
		 * @return void
		 */ 
		public static function executeSOAPEnveloppeBatch(serviceCalled:LinkSession, enveloppe:String, success:Function = null, failed:Function = null):void{
			service.send(new XML(enveloppe));
			if(Log.isDebug()){
				logger.debug("SOAP Enveloppe defined by user has been sent");
			}
			initSOAPEnveloppe();
			manageListeners(serviceCalled, success, failed);
		}
		
		/**
		 * Add or Remove listeners execute after traitment
		 * @param LinkSession instance of service initiated
		 * @param Function Success function callback
		 * @param Function Failed function callback
		 */ 
		private static function manageListeners(serviceCalled:LinkSession, success:Function, failed:Function):void{
			if(serviceCalled != null){
				if(successFunction != null){
					serviceCalled.removeEventListener(ResultSOAPEvent.EVENT_ID, successFunction);
				}
				if(success != null){
					successFunction = success;
					serviceCalled.addEventListener(ResultSOAPEvent.EVENT_ID, successFunction);
					if(Log.isDebug()){
						logger.debug("Callback success function is " + successFunction);
					}
				}
				if(failedFunction != null){
					serviceCalled.removeEventListener(FaultSOAPEvent.EVENT_ID, failedFunction);
				}
				if(failed != null){
					failedFunction = failed;
					serviceCalled.addEventListener(FaultSOAPEvent.EVENT_ID, failedFunction);
					if(Log.isDebug()){
						logger.debug("Callback failed function is " + successFunction);
					}
				}
			}
		}
		
		/**
		 * Use this function to get SOAP Enveloppe sent
		 * @return String value of SOAP Enveloppe request
		 */ 
		public static function getSOAPEnveloppe():String{
			return documentXML.toString();
		}
		
		/**
		 * Use this function to get SOAP Enveloppe response
		 * @return String value of SOAP Envelopppe response
		 */ 
		public static function getSOAPResponse():String{
			return xmlResponseEnveloppe.toString();
		}
		
		/**
		 * Generate a unique ID to identify operation
		 * @return String ID
		 */ 
		private static function getUniqueSOAPResponseID():String{
			uniqueSOAPResponseID++;
			return String(uniqueSOAPResponseID);
		}
		
		/**
		 * Create header of SOAP enveloppe
		 * @param String value to ovverride header SOAP enveloppe
		 * @return String The beginning of SOAP enveloppe
		 */ 
		public static function setEnveloppeSOAPHeader(value:String = null):String{
			if(value == null){
				var xmlEnveloppe:String = "";
				xmlEnveloppe += '<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope" xmlns:elink="http://www.nextapplication.com">';
				xmlEnveloppe += '<SOAP-ENV:Body>';
				return xmlEnveloppe;
			}
			else{
				return value;
			}
		}
		
		/**
		 * Create footer of SOAP enveloppe
		 * @param String value to ovverride footer SOAP enveloppe
		 * @return String The ending of SOAP enveloppe
		 */ 
		public static function setEnveloppeSOAPFooter(value:String = null):String{
			if(value == null){
				var xmlEnveloppe:String = "";
				xmlEnveloppe += '</SOAP-ENV:Body>';
				xmlEnveloppe += '</SOAP-ENV:Envelope>';
				return xmlEnveloppe;
			}
			else{
				return value;
			}
		}
		
		/**
		 * Check if response contains errors or not
		 * @param XML value of response (XML)
		 * @return Boolean contains errors or not
		 */ 
		private function responseTreatment(xmlDocument:XML):Boolean{
			var errorNode:QName = new QName("http://schemas.xmlsoap.org/soap/envelope","Fault");
			var faultNode:XMLList = selectSingleNodes(xmlDocument, errorNode);
			if(faultNode != null && faultNode.length() > 0){
				var faultError:ErrorXML = new ErrorXML(faultNode.faultid, faultNode.faultcode, faultNode.faultstring);
				dispatchEvent(new FaultSOAPEvent(faultError));
				return false;
				if(Log.isDebug()){
					logger.debug("Response failed " + faultNode.toString());
				}
			}
			return true;
		}
		
		/**
		 * Use this function to select a node from XML document with a particulary QName
		 * @param XML document to search node
		 * @param QName value of node to search
		 * @return XMLList value of XML node selected
		 */ 
		private static function selectSingleNodes(xmlDocument:XML, path:QName):XMLList{
			return xmlDocument.descendants(path);
		}
		
		/**
		 * Use this function to get value of the field
		 * @param Object is object retrieved.
		 * @param String is value of field we want to get.
		 * @param TargetEnum is detail target
		 * @param String value in case of field is null 
		 * @return String value of field
		 */ 
		public static function getFieldValue(object:Object, field:String, nullValue:String = null):String{
			if(object != null){
				if(!(object.MASTER[field.toUpperCase()] is ObjectProxy)){
					return object.MASTER[field.toUpperCase()];
				}
				else{
					if(nullValue != null && object.MASTER[field.toUpperCase()].ISNULL == true){
						return nullValue;
					}
				}
				
			}
			return "";
		}
		
		/**
		 * Use this function to set value of the field
		 * @param Object is object retrieved.
		 * @param String is value of field we want to set.
		 * @return void
		 */
		public static function setEditFieldValue(listToEdit:ArrayCollection, field:String, value:String):void{
			if(listToEdit == null){
				listToEdit = new ArrayCollection();
			}
			listToEdit.addItem(new ObjectValueXML(field, value));
		}
		
		/**
		 * Use this function to set value of the field when you want edit an entity
		 * @param ArrayCollection is collection of fields to update.
		 * @param String is value of field we want to set.
		 * @return void
		 */
		public static function setFieldValue(object:Object, field:String, value:Object):void{
			if(object != null){
				object.MASTER[field.toUpperCase()] = value;
			}
		}
		
		/**
		 * Use this function to get entries of detail
		 * @param Object is object retrieved.
		 * @param TargetEnum is detail target
		 * @return ArrayCollection lsit of entries
		 */ 
		public static function getDetail(object:Object, detail:TargetEnum):ArrayCollection{
			if(object[detail.getTargetName()] != null && object[detail.getTargetName()].length > 0){
				return object[detail.getTargetName()] as ArrayCollection;
			}
			else{
				return new ArrayCollection();
			}
		}
		
		/**
		 * Use this function to get value of the detail field
		 * @param Object is object retrieved.
		 * @param String is value of field we want to get.
		 * @param TargetEnum is detail target
		 * @param Number is index of detail
		 * @param String value in case of field is null 
		 * @return String value of field
		 */ 
		public static function getDetailFieldValue(object:Object, detail:TargetEnum, field:String, indexDetail:Number = 0, nullValue:String = null):String{
			if(object != null){
				if(detail != null){
					if(object[detail.getTargetName()] is ArrayCollection && (object[detail.getTargetName()] as ArrayCollection).length > 0){
						if(!(object[detail.getTargetName()][indexDetail][field.toUpperCase()] is ObjectProxy)){
							return object[detail.getTargetName()][indexDetail][field.toUpperCase()];
						}
						else{
							if(nullValue != null && object[detail.getTargetName()][indexDetail][field.toUpperCase()].ISNULL == "true"){
								return nullValue;
							}
						}
					}
				}
			}
			return "";
		}
		
		/**
		 * Use this function to set value of the detail field
		 * @param Object is object retrieved.
		 * @param TargetEnum is detail target
		 * @param String is value of field we want to set.
		 * @param Object value to set
		 * @param Number is index of detail
		 * @return void
		 */ 
		public static function setDetailFieldValue(object:Object, detail:TargetEnum, field:String, value:Object, indexDetail:Number = 0):void{
			if(object != null){
				if(object[detail.getTargetName()] is ArrayCollection && (object[detail.getTargetName()] as ArrayCollection).length > 0){
					object[detail.getTargetName()][indexDetail][field.toUpperCase()] = value;
				}
			}
		}
		
		/**
		 * Use this function to get value of the detail field
		 * @param Object is object retrieved.
		 * @param DataGridColumn is column affected
		 * @return String value of field
		 */ 
		public static function labelFieldFunction(item:Object, column:DataGridColumn):String{
			if(!(item[column.dataField] is ObjectProxy)){
				return item[column.dataField];
			}
			else{
				return "";
			}
		}
		
		/**
		 * Get data from ID generated. 
		 * @param Number id to identify response of request
		 * @return ArrayCollection Object list of response
		 */ 
		public function getListData(id:Number, initParam:Boolean = true):ArrayCollection{
			var result:ArrayCollection = new ArrayCollection();
			var faultError:ErrorXML;
			if(xmlResponseEnveloppe != null){	
				var listChild:XMLList = xmlResponseEnveloppe.descendants().(attribute("ID") == String(id));
				if(listChild.length() > 0){
					var selected:XML = listChild[0];
					if(selected.attribute("handled").contains("true")){
						if(datacontainers.length > 0){
							var container:Object = new Object();
							for (var i:int=0; i<datacontainers.length; i++){
								if(String(datacontainers[i]).toUpperCase() == "MASTER"){
									container[datacontainers[i]] = getDataFromDocument(selected, i).getItemAt(0);
								}
								else{
									container[datacontainers[i]] = getDataFromDocument(selected, i);
								}
							}
							result.addItem(container);
							if(Log.isDebug()){
								logger.debug("DataContainer Array value :" + datacontainers.toString());
							}
						}
						else{
							result = getDataFromDocument(selected);
						}
					}
					else{
						faultError = new ErrorXML(null, "SOAP Error", "Exception while creating Object, SOAP request not handled");
						dispatchEvent(new FaultSOAPEvent(faultError));
					}
				}
				else{
					faultError = new ErrorXML(null, "SOAP Error", "Exception while creating Object, id passed in argument is invalid");
					dispatchEvent(new FaultSOAPEvent(faultError));
				}
			}
			//Init datacontainer
			if(initParam){
				initAttributSOAPEnveloppe();
			}
			return result;
		}
		
		/**
		 * This function return a single object of result
		 * @param Number id to identify response of request
		 * @return Object Object of response
		 */
		public function getGlobalData(id:Number):Object{
			var list:ArrayCollection = this.getListData(id);
			return list.getItemAt(0) as Object;
		}
		
		/**
		 * This function retrieve list of data (entity called is datacontainer)
		 * @param Number id to identify response of request
		 * @return ArrayCollection Object list of response
		 */  
		public function getSingleListData(id:Number):ArrayCollection{
			var returnList:ArrayCollection;
			var list:ArrayCollection = this.getListData(id, false);
			if(datacontainers.length > 0){
				if(Log.isDebug()){
					logger.debug("DataContainer Array value :" + datacontainers.toString());
				}
				returnList = (list.getItemAt(0) as Object)[datacontainers[0]] as ArrayCollection;
			}
			else{
				var faultError:ErrorXML = new ErrorXML(null, "SOAP Error", "Exception while creating Object, datacontainer is empty");
				dispatchEvent(new FaultSOAPEvent(faultError));
				returnList = null;
			}
			initAttributSOAPEnveloppe();
			return returnList;
		}
		
		/**
		 * This function retrieve list of data (entity called is data)
		 * @param Number id to identify response of request
		 * @return ArrayCollection Object list of response
		 */   
		public function getInformationData(id:Number):ArrayCollection{
			var result:ArrayCollection = new ArrayCollection();
			var resultObject:Object;
			var faultError:ErrorXML;
			if(xmlResponseEnveloppe != null){	
				var listChild:XMLList = xmlResponseEnveloppe.descendants(new QName("http://www.nextapplication.com","common")).(attribute("ID") == String(id));
				if(listChild.length() > 0){
					resultObject = decoder.decodeXML(new XMLDocument(listChild.toString()));
					if(resultObject != null){
						if(resultObject.common.data is ArrayCollection){
							result = resultObject.common.data;
						}
						else{
							result.addItem(resultObject.common.data);
						}
					}
				}
				else{
					faultError = new ErrorXML(null, "SOAP Error", "Exception while creating Object, id passed in argument is invalid");
					dispatchEvent(new FaultSOAPEvent(faultError));
				}
			}
			return result;
		}
		
		/**
		 * This function return a single object of result (entity called is data)
		 * @param Number id to identify response of request
		 * @return Object Object of response
		 */
		public function getSingleInformationData(id:Number):Object{
			var faultError:ErrorXML;
			var list:ArrayCollection = this.getInformationData(id);
			if(list.length > 0){
				return list.getItemAt(0) as Object;
			}
			else{
				faultError = new ErrorXML(null, "SOAP Error", "Exception while creating Object, id passed in argument is invalid");
				dispatchEvent(new FaultSOAPEvent(faultError));
				return null;
			}
		}
		
		/**
		 * This function return a boolean to know if we have one result
		 * @param Number id to identify response of request
		 * @return Boolean
		 */
		public function fetchOneResult(id:Number):Boolean{
			var list:ArrayCollection = this.getListData(id, false);
			if(list.length == 1){
				return true;
			}
			return false;
		}
		
		/**
		 * This function analyse response denpending of type of response
		 * @param XML document to analyse
		 * @param Number Index of Document
		 * @return ArrayCollection list of objects
		 */ 
		private static function getDataFromDocument(documentXML:XML, index:Number = 0):ArrayCollection{
			var result:ArrayCollection = new ArrayCollection();
			var resultObject:Object;
			var listDoc:XMLList = documentXML.descendants("DOCUMENT");
			var contentDoc:XML = listDoc[index];
			if(contentDoc.length() > 0){
				var classDoc:String  = contentDoc.@CLASS;
				if(classDoc.toUpperCase() == "DATASET" || classDoc.toUpperCase() == "COLLECTION"){
					resultObject = decoder.decodeXML(new XMLDocument(contentDoc.descendants("ROWS").toString()));
					if(resultObject != null){
						if(resultObject.ROWS.ROW is ArrayCollection){
							result = resultObject.ROWS.ROW;
						}
						else{
							result.addItem(resultObject.ROWS.ROW);
						}
					}
				}
				else if(classDoc.toUpperCase() == "STRINGLIST"){
					resultObject = decoder.decodeXML(new XMLDocument(contentDoc.descendants("ROWS").toString()));
					result = resultObject.ROWS.ROW;
				}
			}
			return result;
		}
	}
}